#include "tss.h"
#include "gdt.h"
#include "mmu.h"
#include "debug.h"
#include "i386.h"
#include "isr.h"
#include "sched.h"

//selectores de la GDT
#define TAREA_FORK_SEL 1
#define TAREA_INICIAL_SEL 3
#define TAREA_IDLE_SEL 5
#define INICIO_TAREAS_SEL 7

tss tarea_inicial;
tss tarea_idle;
tss tsss[TSS_COUNT];
tss tarea_fork;
tss tarea_mondrian;

uint tss_size = sizeof(tss)-1;


void inicializar_tss_desc(){
    //DESCRIPTORES IDLE e INICIAL
    cargar_tss_desc(TAREA_INICIAL_SEL, &tarea_inicial);
    cargar_tss_desc(TAREA_IDLE_SEL, &tarea_idle);
    
    cargar_tss_desc(TAREA_FORK_SEL, &tarea_fork);

    //DESCRIPTORES TAREAS WORM
    uint i=0;
    for( ; i< TSS_COUNT; i++){
        cargar_tss_desc(i + INICIO_TAREAS_SEL, &(tsss[i]));                   // 6 es la ultima gdt_entry usada para todo menos las tareas worm
    }

    // NO cargamos la tss de TAREA_INICIAL porque el procesador lo va a hacer solo
    // cuando se produzca el primer cambio de tarea

    //CARGAMOS TSS IDLE
    uint p = (uint)pagina_libre_usuario();
    mapear_pagina(p, rcr3(), p);
    
    cargar_tss(&tarea_idle, rcr3(), p, TASKIDLE);
    
    //CARGAMOS TSS FORK
    cargar_tss(&tarea_fork, rcr3(), (uint)pagina_libre_kernel(), (uint)&_isr66);
    tarea_fork.eflags = 0x002;

    
    //CARGAMOS MONDRIAN
    cargar_tss_desc(INICIO_TAREAS_SEL + CANT_TAREAS, &tarea_mondrian); 
}

void cargar_tss_desc(uint index, tss* dir_tarea){
    gdt[index].limit_0_15 = tss_size & 0xFFFF;
    gdt[index].limit_16_19 = (tss_size >> 16) & 0xF;
    gdt[index].type = 0x9;                               // tss type con busy=0 (01001b)
    gdt[index].s = 0;                                    // descriptor de sistema
    gdt[index].p = 1;                                    // siempre presente
    gdt[index].db = 0;
    gdt[index].g = 1;                                    // limite expresado en bytes
    gdt[index].dpl = 0;
    gdt[index].avl = 0;
    gdt[index].l = 0;
    gdt[index].base_31_24 = ((uint)dir_tarea >> 24) & 0xFF;
    gdt[index].base_0_15 = (uint)dir_tarea & 0xFFFF;
    gdt[index].base_23_16 = ((uint)dir_tarea >> 16) & 0xFF;
}


void cargar_tss(tss* tarea_tss, uint cr3,uint pila, uint eip){
    //Crea una estructura de paginacion para la tarea
    tarea_tss->cr3 = cr3;
    
    //me paro al final de la pila porque crece hacia las posiciones menores
	tarea_tss->ebp = tarea_tss->esp = pila + 4096;
	tarea_tss->eip = eip;

	tarea_tss->eflags = 0x202;
    tarea_tss->iomap = 0xFFFF;

	tarea_tss->cs = 0x30;
    tarea_tss->ss = 0x20;
	tarea_tss->ds = 0x20;
	tarea_tss->fs = 0x20;
	tarea_tss->gs = 0x20;
	tarea_tss->es = 0x10;


    tarea_tss->ptl=0;
	tarea_tss->eax=0;
	tarea_tss->ecx=0;
	tarea_tss->edx=0;
	tarea_tss->ebx=0;
	tarea_tss->esi=0;
	tarea_tss->edi=0;
	tarea_tss->ldt=0;
	tarea_tss->dtrap=0;
    
    //como trabajamos todo en anillo cero
    //no es necesario setear esto porque no va 
    //a haber pila por nivel
	tarea_tss->esp1=0;
	tarea_tss->ss1=0;
	tarea_tss->esp2=0;
	tarea_tss->ss2=0;
	tarea_tss->esp0=0;
	tarea_tss->ss0=0;

	tarea_tss->unused0=0;
	tarea_tss->unused1=0;
	tarea_tss->unused2=0;
	tarea_tss->unused3=0;
	tarea_tss->unused4=0;
	tarea_tss->unused5=0;
    tarea_tss->unused6=0;
	tarea_tss->unused7=0;
	tarea_tss->unused8=0;
	tarea_tss->unused9=0;
	tarea_tss->unused10=0;

}

void duplicar_tss(tss *src, tss *dest, uint cr3){
    uint* src_i = (uint*) src;
    uint* dest_i = (uint*) dest;
    //~ 
    int i = sizeof(tss)/4 - 1;

    while(i >= 0){
        dest_i[i] = src_i[i];
        i--;
    }
  
    dest->cr3 = cr3;
}
